package org.example.sync.guarded;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhengdp
 * @Description 扩展2，支持多个任务的管理
 *               这边是 People 和 Postman 一一对应的形式
 * @Date 2022/10/16 22:03
 * @Created by zhengdp
 */
@Slf4j(topic = "c.GuardedTestV2")
public class GuardedTestV2 {
    @SneakyThrows
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new People().start();
        }
        Thread.sleep(1000);
        for(int id : Mailboxes.getIds()){
            new Postman(id,"内容" + id).start();
        }
    }

}
@Slf4j(topic = "c.People")
class People extends Thread{
    @Override
    public void run() {
        // 收信
        GuardedObjectV2 guardedObjectV2 = Mailboxes.createGuardedObject();
        log.info("开始 收信 id:{}",guardedObjectV2.getId());
        Object mail = guardedObjectV2.get(5000);
        log.info("收到信 id:{},内容:{}",guardedObjectV2.getId(),mail);
    }
}

@Slf4j(topic = "c.Postman")
class Postman extends Thread {

    private int mailId;
    // 一般邮递员不会自己产生信的内容，这边简化了
    private String mail;

    public Postman(int mailId, String mail) {
        this.mailId = mailId;
        this.mail = mail;
    }

    @Override
    public void run() {

        GuardedObjectV2 guardedObject = Mailboxes.getGuardedObject(mailId);
        log.info("送信 id:{},内容 :{}",guardedObject.getId(),mail);
        // 产生结果
        guardedObject.complete(mail);
    }
}

/**
 * 中间类，用于解耦
 */
class Mailboxes {
    // 该集合会被多个线程访问到，所以要保证这个集合的线程安全性
    private  final static  Map<Integer , GuardedObjectV2> boxes = new ConcurrentHashMap<>();


    private static int id = 1;

    /**
     * 产生唯一 id (多个线程调用该方法，需要保证该方法的线程安全 ==> 返回不重复的Id
     * 也可以使用 UUID
     * @return {@link int}
     */
    public static synchronized  int generateId(){
        return id++;
    }

    /**
     * 返回 GuardedObjectV2 对象
     * @return  {@link GuardedObjectV2}
     */
    public static GuardedObjectV2 createGuardedObject(){
        GuardedObjectV2 go = new GuardedObjectV2(generateId());
        boxes.put(go.getId(),go);
        return go;
    }

    public static Set<Integer> getIds(){
        return boxes.keySet();
    }

    public static GuardedObjectV2 getGuardedObject(int id){
        return boxes.remove(id);
    }
}

class GuardedObjectV2 extends GuardedObject {
    private int id;

    public GuardedObjectV2(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}